package sc.client;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import k8.Logic;

import sc.client.world.Avatar;
import sc.client.world.Container;
import sc.client.world.Tile;
import sc.datainterchange.CodedInputBuffer;
import sc.datainterchange.CodedOutputBuffer;
import sc.nio.NioConnection;

public class ServerIO extends Logic
{
	// The NioConnection
	private NioConnection nio;
	
	// Coded data buffer received from server
	private final CodedInputBuffer codedinput = new CodedInputBuffer();
	
	// Coded data buffer to write to server
	public static final int DEFAULT_BUFFER_SIZE = 4096;
	private final byte[] outputbuffer = new byte[DEFAULT_BUFFER_SIZE];
	private final CodedOutputBuffer codedoutput = new CodedOutputBuffer(outputbuffer);
	
	/**
	 * Creates a ProtocolBuffer instance.
	 * @throws IOException 
	 */
	public ServerIO(NioConnection nio) throws IOException
	{
		this.nio = nio;
		this.codedoutput.writeString(Client.avatar.name);
		setUpdateRate(1);
	}
	
	@Override
	public void update(long time)
	{
		byte[] buffer;
		int tag;
		
		try
		{
			// Read pending received data
			while ((buffer = this.nio.read()) != null)
			{
				this.codedinput.put(buffer);
				while ((tag = this.codedinput.readTag()) != 0)
				{
					switch (tag)
					{
					case Protocol.OBJECT:
						readObject();
						break;
					case Protocol.MESSAGE:
						readMessage();
						break;
					case Protocol.DISCONNECT:
						readDisconnect();
						break;
					case Protocol.AUTHENTICATE:
						readAuthenticate();
						break;
					case Protocol.AUTHENTICATED:
						readAuthenticated();
						break;
					case Protocol.CONNECT:
						readConnect();
						break;
					}
				}
			}
			
			// Write pending data
			int numWrite = this.codedoutput.position();
			if (numWrite > Client.avatar.name.length() + 1)
			{			
				byte[] dataCopy = new byte[numWrite];
	            System.arraycopy(this.outputbuffer, 0, dataCopy, 0, numWrite);
				this.nio.send(dataCopy);
				this.codedoutput.clear();
				this.codedoutput.writeString(Client.avatar.name);
			}
		}
		catch(IOException e)
		{
			Client.logger.warning(e.getMessage());
		}
	}

	private void readAuthenticate() throws IOException
	{
		Client.logger.info("Authenticating with server...");
		this.writeAuthenticate();
	}

	private void readAuthenticated() throws IOException
	{
		Client.logger.info("Authenticated with server.");
	}

	private void readMessage() throws IOException
	{
		Client.logger.info(this.codedinput.readString());
	}

	private void readConnect()
	{
		Client.logger.info("Connected to server");
	}

	/**
	 * Called when the server has asked the client to disconnect.
	 * @throws IOException 
	 */
	private void readDisconnect() throws IOException
	{
		Client.logger.info("Disconnected from server: " + this.codedinput.readString());
	}

	/**
	 * Called when an "object" message is received to materialise an Object
	 * being sent from the server.
	 * @throws IOException 
	 */
	private void readObject() throws IOException
	{
		Container object = null;
		String type = this.codedinput.readString();
		String id = this.codedinput.readString();
		String key = type + id;

		if (Client.containers.containsKey(key))
			object = Client.containers.get(key).get(0);
		else
		{
			if (type.equals("tile"))
				object = new Tile();
			else if (type.equals("avatar"))
				object = new Avatar(id);
		}
		
		object.read(this.codedinput);
		
		// Add to all known containers if not already
		List<Container> objectlist = new ArrayList<Container>();
		objectlist.add(object);
		Client.containers.put(object.getID(), objectlist);
	}
	
	public void writeConnect() throws IOException
	{
		this.codedoutput.writeTag(Protocol.CONNECT);
	}
	
	public void writeAuthenticate() throws IOException
	{
		this.codedoutput.writeTag(Protocol.AUTHENTICATE);
		this.codedoutput.writeString(Client.properties.getProperty("user.password"));
	}
}
